---
title: useDraggable
description: The useDraggable hook is used to drag elements through mouse, touch, and keyboard events and was created for to support the WindowSplitter and Slider components. This is NOT a drag and drop hook.
docType: API Docs
docGroup: Hooks
group: Low-level
hooks: [useDraggable]
---

# useDraggable [$SOURCE](packages/core/src/draggable/useDraggable.ts)

```ts disableTransform
function useDraggable<E extends HTMLElement>(
  options: DraggableOptions<E>
): DraggableImplementation<E>;
```

> !Error! This is **not** a drag and drop hook.

The `useDraggable` hook is used to drag elements through mouse, touch, and
keyboard events and was created for to support the
[WindowSplitter](/components/window-splitter) and [Slider](/components/slider)
components.

## Example Usage

Here's a quick example using one of the
[useDraggable tests]($GITHUB/packages/core/src/draggable/__tests__/useDraggable.tsx).

```tsx
function Example() {
  const [value, setValue] = useState(20);
  const [dragging, setDragging] = useState(false);

  const {
    draggableRef,
    keyboardEventHandlers,
    minimum,
    maximum,
    increment,
    decrement,
  } = useDraggable({
    min: 0,
    max: 100,
    dragging,
    value,
    setValue,
    setDragging,
  });

  const percentage = getPercentage({ min: 0, max: 100, value });

  return (
    <>
      <Button
        aria-valuenow={Math.ceil(percentage * 100)}
        ref={draggableRef}
        {...keyboardEventHandlers}
        className={cnb(dragging && "dragging")}
      >
        Button
      </Button>
      <Button onClick={minimum}>Minimum</Button>
      <Button onClick={maximum}>Maximum</Button>
      <Button onClick={increment}>Increment</Button>
      <Button onClick={decrement}>Decrement</Button>
    </>
  );
}
```

## Parameters

- `options` - An object with the following definition:

````ts disableTransform
export type DraggableOptions<E extends HTMLElement = HTMLElement> =
  BaseDraggableOptions<E> & DraggableStateOptions;

export interface BaseDraggableOptions<E extends HTMLElement>
  extends DraggableEventHandlers<E>, ControllableDraggableStateOptions {
  /**
   * An optional ref to merge with the returned
   * {@link DraggableImplementation.draggableRef}.
   */
  ref?: Ref<E>;

  /**
   * The minimum number of pixels allowed for the draggable element. This must
   * be a number greater than or equal to 0.
   *
   * When {@link withinOffsetParent} is set to `true`, this is the minimum value
   * allowed instead of pixels.
   */
  min: number;

  /**
   * The maximum number of pixels allowed for the draggable element. This must
   * be a number greater than the {@link min} and usually a number less than the
   * viewport size.
   *
   * When {@link withinOffsetParent} is set to `true`, this is the maximum value
   * allowed instead of pixels.
   */
  max: number;

  /**
   * The amount to increment or decrement the value with arrow keys.
   *
   * @defaultValue `1`
   */
  step?: number;

  /**
   * This was added to support range sliders where there are two (or more)
   * draggable elements within the same container element and their values
   * cannot pass each other. Without these overrides, the range would keep
   * changing as the other values change, so the drag percentage would be
   * incorrect.
   *
   * @example Range Slider
   * ```ts
   * const min = 0;
   * const max = 100;
   * const minValue = 3;
   * const maxValue = 80;
   *
   * const minValueDraggable = useDraggable({
   *   min,
   *   max,
   *   rangeMax: maxValue,
   * });
   * const maxValueDraggable = useDraggable({
   *   min,
   *   max,
   *   rangeMin: minValue,
   * });
   * ```
   *
   * @defaultValue `min`
   */
  rangeMin?: number;

  /**
   * @see {@link rangeMin} for an explanation of this option.
   * @defaultValue `max`
   */
  rangeMax?: number;

  /**
   * Set this to `true` to enable dragging vertically instead of horizontally.
   *
   * @defaultValue `false`
   */
  vertical?: boolean;

  /**
   * The default drag behavior is to increase the value when:
   *
   * - dragging `"right"` and the writing direction is `"ltr"`
   * - dragging `"left"` and the writing direction is `"rtl"`
   * - dragging `"upwards"`
   *
   * When this is set to `true`, the value when increase when:
   *
   * - dragging `"left"` and the writing direction is `"ltr"`
   * - dragging `"right"` and the writing direction is `"rtl"`
   * - dragging `"downwards"`
   *
   * @defaultValue `false`
   */
  reversed?: boolean;

  /**
   * Set this to `true` to disable all drag behavior. This will still call any
   * of the provided {@link DraggableEventHandlers}.
   *
   * @defaultValue `false`
   */
  disabled?: boolean;

  /**
   * Set this to `true` if the dragging calculations should be to the
   * `draggableRef.current.offsetParent` instead of the entire window. The main
   * use case for this is sliders.
   *
   * @defaultValue `false`
   */
  withinOffsetParent?: boolean;

  /**
   * Set this to `true` to prevent the `document.documentElement` from gaining
   * the `.rmd-dragging` class names while dragging.
   *
   * This should normally remain as `false` to improve performance and prevent
   * other mouse events from firing while dragging.
   *
   * @defaultValue `false`
   */
  disableDraggingClassName?: boolean;

  /**
   * Set this to `true` to prevent the vertical or horizontal cursor from
   * appearing while dragging.
   *
   * @defaultValue `false`
   */
  disableDraggingCursorClassName?: boolean;
}
````

## Returns

````ts disableTransform
export interface DraggableImplementation<
  E extends HTMLElement = HTMLElement,
> extends Required<DraggableEventHandlers<E>> {
  mouseEventHandlers: Required<DraggableMouseEventHandlers<E>>;
  touchEventHandlers: Required<DraggableTouchEventHandlers<E>>;
  keyboardEventHandlers: Required<DraggableKeyboardEventHandlers<E>>;

  /**
   * Set the {@link value} to {@link DraggableOptions.min}.
   */
  minimum: () => void;

  /**
   * Set the {@link value} to {@link DraggableOptions.max}.
   */
  maximum: () => void;

  /**
   * Increment the {@link value} by {@link DraggableOptions.step}.
   */
  increment: () => void;

  /**
   * Decrement the {@link value} by {@link DraggableOptions.step}.
   */
  decrement: () => void;

  /**
   * The current percentage the `value` is within the range.
   *
   * ```ts
   * const percentage =
   *   dragging && withinOffsetParent
   *     ? : dragPercentage
   *     : getPercentage({ min, max, value });
   * ```
   */
  percentage: number;

  /**
   * A ref that **Must** be passed to the element that should be draggable.
   */
  draggableRef: RefCallback<E>;

  /**
   * This value will only update while dragging with a mouse or touch and should
   * be used for the positioning styles while dragging.
   *
   * Note: The {@link percentage} will use this value while dragging.
   */
  dragPercentage: number;

  /**
   * Flag to determine if the user has dragged at least once. Used internally
   * for manually persisting the value into local storage once the user has
   * stopped dragging.
   */
  draggedOnce: NonNullRef<boolean>;

  value: number;
  setValue: UseStateSetter<number>;
  dragging: boolean;
  setDragging: UseStateSetter<boolean>;
}
````

[useDraggable tests]: {GITHUB_LINK_URL}
